import java.io.*;
import java.util.*;

public class Simulator {

    //private long startTime;
 
    private static int endTime;
    private static int simTime;

    private static int maxItems;
    private static int scanSpeed;

    private static BooleanSource customerArrival;
    private static Queue[] queues;
    private static ArrayList<Integer> waitTimes;

    public static double simulate(int numQueues, double arrivalProb, int itemLimit, int speed, int simMinutes) {
	if (numQueues < 2 || arrivalProb > 1 || arrivalProb < 0 || itemLimit < 1 || speed < 1 || simMinutes < 1) {
	    System.out.println(numQueues + ", " + arrivalProb + ", " + itemLimit + ", " + speed + ", " + simMinutes);
	
	    throw new IllegalArgumentException("Invalid arguments provided.");
	}
	//startTime = System.currentTimeMillis();
	endTime = simMinutes * 60;
        simTime = 0;
	maxItems = itemLimit;
	scanSpeed = speed;

	queues = new Queue[numQueues]; 
	for (int i = 0; i < numQueues; i++) {
	    queues[i] = new Queue();
	    queues[i].serveNextCustomerAt = 0;
	}

	try { customerArrival = new BooleanSource(arrivalProb);}
	catch(IllegalArgumentException e){}
	waitTimes = new ArrayList<Integer>();

	while(simTime != endTime) {
	    simulateSecond();
	    simTime++;
	}
	return calcAvgTime();
    }

    private static void simulateSecond() {

	for (int i = 0; i < queues.length; i++) {
	    // System.out.println("......"+ queues[i].serveNextCustomerAt);
	    if (queues[i].serveNextCustomerAt <= simTime) {
		Customer finishedCustomer = (Customer)(queues[i].dequeue());
		if (finishedCustomer == null)
		    continue;
		waitTimes.add((int)(simTime - finishedCustomer.getArrivalTime()));
		//System.out.println(simTime + ", "+ finishedCustomer.getArrivalTime());
		try { Customer temp = (Customer)queues[i].firstElement();
		    queues[i].serveNextCustomerAt = simTime + (temp.getNumItems() * scanSpeed);
		    //System.out.println("~~~~~~"+queues[i].serveNextCustomerAt);}
//queues[i].serveNextCustomerAt = simTime + (queues[i].firstElement().numItems * scanTime);}
		catch(NoSuchElementException e) {}
	    }
	}
	if (customerArrival.occurs())
	    addCustomer();
    }

    private static double calcAvgTime() {
	int sum;
	double avg;

	sum = 0;
	for(int i = 0; i < waitTimes.size(); i++) {
	    sum += waitTimes.get(i);
	}
	avg = sum / waitTimes.size();
	System.out.println(waitTimes.size());
	//for (int i = 0; i < 200; i++) {
	//   System.out.print(waitTimes.get(i)+" , ");
	}
	return avg;
    }

    private static void addCustomer() {
	int numItems;
	double random;
	int prefferedQueue;
	Customer newCust;

	random = Math.random();
	if (random < .5) 
	    numItems = (int)(Math.random() * 6);
	else 
	    numItems = (int)(Math.random() * (maxItems - 5) + 6);
        newCust = new Customer(simTime, numItems);

	if (numItems <= 15)
	    prefferedQueue = 0;
	else prefferedQueue = 1;
        for (int i = 1; i < queues.length; i++) {
	    if (prefferedQueue == 0) {
		if (queues[i].size() * 2 < queues[prefferedQueue].size())
		    prefferedQueue = i;
	    }
	    else {
		if (queues[i].size() < queues[prefferedQueue].size())
		    prefferedQueue = i;
	    }
	}
	queues[prefferedQueue].enqueue(newCust);
    }

}
